using System;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Xml;
using System.Xml.Schema;
using OpenComposite.HumanActivity.Contracts;
using OpenComposite.HumanActivity.Data;

namespace OpenComposite.HumanActivity.Services
{
	[ServiceBehavior(
		ConcurrencyMode = ConcurrencyMode.Multiple,
		InstanceContextMode = InstanceContextMode.Single,
		Namespace = "http://www.theopencomposite.com/HumanActivity")]
	public class HumanActivityService : WsdlExporter, IHumanActivityService
	{
		#region Initialization
		public HumanActivityService(DataRow row, EventLog eventLog)
		{
			_row = row;
			_eventLog = eventLog;
		}
		#endregion

		#region Protected Properties
		protected int HumanActivityId
		{
			get { return (int)_row["hact_id"]; }
		}
		#endregion

		#region Private Fields
		private DataRow _row;
		private EventLog _eventLog;
		#endregion

		#region IHumanActivityService Members

		public void HumanActivityStart(System.Xml.XmlNode data)
		{
			ActivityItem ai = null;
			try {
				ai = OperationContext.Current.IncomingMessageHeaders.GetHeader<ActivityItem>(
					"ActivityItem", "http://www.theopencomposite.com/HumanActivity");
				if ( ai == null ) {
					throw Global.CreateAndLogHumanActivityFault(
						"MessageHeader", "Missing activity information header.", null, -1, -1);
				}
			} catch ( MessageHeaderException mhex ) {
				throw Global.CreateAndLogHumanActivityFault("MessageHeader", mhex);
			} catch ( FaultException<HumanActivityFault> ) {
				throw;
			} catch ( Exception ex ) {
				throw Global.CreateAndLogHumanActivityFault("UnknowError", ex);
			}

			int activityId;
			try {
				if ( !Global.DataAccess.CreateActivity(ai.WorkflowInstanceId, ai.ActivityName,
						ai.ProcessName, ai.Subject, this.HumanActivityId,
						OperationContext.Current.IncomingMessageHeaders.ReplyTo.ToString(),
						OperationContext.Current.IncomingMessageHeaders.MessageId.ToString(),
						ai.Due ?? DateTime.Now.AddDays(1),
						out activityId) ) {
					throw Global.CreateAndLogHumanActivityFault("CreateActivity",
						 new Exception("Can't create new human activity."));
				}
			} catch ( Exception ex1 ) {
				throw Global.CreateAndLogHumanActivityFault("CreateActivity", ex1);
			}
			try {
				Global.DataAccess.SetActivityInputData(activityId, ai.ActivityUserId ?? -1, data);
				Global.DataAccess.AddParticipants(activityId, ai.Participants);
			} catch ( Exception ex2 ) {
				throw Global.CreateAndLogHumanActivityFault("SetActivityInputData", ex2);
			}
		}


		#endregion

		#region WsdlExporter

		public override MetadataSet GetGeneratedMetadata()
		{
			MetadataSet metaset = base.GetGeneratedMetadata();
			System.Web.Services.Description.ServiceDescription wsdl = getWSDL(metaset);
			if ( wsdl == null ) return metaset;	// EXIT

			int pos;
			string dialect, identifier;
			XmlSchema xsd = getXSD(metaset, out pos, out dialect, out identifier);
			if ( xsd == null ) return metaset;	// EXIT

			XmlDocument xsdForm = getFormSchemaAsXmlDoc();
			if ( xsdForm == null ) return metaset;	// EXIT

			XmlDocument xdSchema = new XmlDocument();
			using ( MemoryStream ms = new MemoryStream() ) {
				xsd.Write(ms);
				ms.Position = 0;
				xdSchema.Load(ms);
			}
			XmlNamespaceManager xnsmgr = new XmlNamespaceManager(xdSchema.NameTable);
			xnsmgr.AddNamespace("xs", XmlSchema.Namespace);
			foreach ( XmlNode xn in xdSchema.SelectNodes("//xs:any", xnsmgr) ) {
				XmlNode xnParent = xn.ParentNode;
				xnParent.RemoveChild(xn);
				xnParent.AppendChild(xdSchema.ImportNode(xsdForm.DocumentElement.FirstChild, true));
			}
			XmlSchema xsdNew;
			using ( MemoryStream ms = new MemoryStream() ) {
				xdSchema.Save(ms);
				ms.Position = 0;
				xsdNew = XmlSchema.Read(ms, null);
			}
			metaset.MetadataSections.RemoveAt(pos);
			metaset.MetadataSections.Add(new MetadataSection(dialect, identifier, xsdNew));

			return metaset;
		}

		#endregion

		#region Private Methods

		private System.Web.Services.Description.ServiceDescription getWSDL(MetadataSet metaset)
		{
			System.Web.Services.Description.ServiceDescription wsdl = null;
			foreach ( MetadataSection section in metaset.MetadataSections ) {
				wsdl = section.Metadata as System.Web.Services.Description.ServiceDescription;
				if ( wsdl != null ) break;
			}
			return wsdl;
		}
		private XmlSchema getXSD(MetadataSet metaset, out int pos, out string dialect, out string identifier)
		{
			pos = -1;
			dialect = "";
			identifier = "";
			XmlSchema xsd = null;

			for ( int i = 0; i < metaset.MetadataSections.Count; i++ ) {
				MetadataSection section = metaset.MetadataSections[i];
				xsd = section.Metadata as XmlSchema;
				if ( xsd != null && xsd.TargetNamespace == "http://www.theopencomposite.com/HumanActivity" ) {
					pos = i;
					dialect = section.Dialect;
					identifier = section.Identifier;
					break;
				} else {
					xsd = null;
				}
			}
			return xsd;
		}
		private XmlDocument getFormSchemaAsXmlDoc()
		{
			const string colSchema = "hact_schema";

			if ( _row == null || _row.IsNull(colSchema) ) return null; // EXIT

			XmlDocument xd = new XmlDocument();
			using ( StringReader sr = new StringReader((string)_row[colSchema]) ) {
				xd.Load(sr);
			}
			return xd;
		}

		#endregion
	}
}
